import { TimeUtil } from "../util/TimeUtil";
import { TypeUtil } from "../util/TypeUtil";
import { ECSEntity } from "./ECSEntity";
import { ECSFilter, ECSFilterEvent, IECSFilter } from "./ECSFilter";
import { ECSScene } from "./ECSScene";

/**
 * [ECS]系统
 * tip:监听scene或filter事件时,caller设为this在移除时会自动清理监听,否则需要手动清理
 */
export abstract class ECSSys {
	/** 更新顺序 更小则比其他sys更早执行update */
	abstract get updateOrder(): number;
	/** 过滤器参数 */
	abstract get ifilter(): IECSFilter;

	/** 过滤器 */
	filter: ECSFilter;
	/** 暂停中 */
	paused: boolean;

	constructor(
		/** 所在场景 */
		readonly scene: ECSScene
	) {}

	// ============ 生命周期 ============

	/**
	 * 启动系统
	 */
	doStart() {
		if (this.filter) {
			if (this.onEntityAdd) {
				this.filter.on(ECSFilterEvent.entityAdd, this.onEntityAdd, this);
			}
			if (this.onEntityRemove) {
				this.filter.on(ECSFilterEvent.entityRemove, this.onEntityRemove, this);
			}
		}
		this.onStart?.();
	}

	/**
	 * 每帧更新
	 */
	doUpdate() {
		if (this.paused) return;
		this.onUpdate();
		if (this.onUpdateEntity) {
			this.filter.entitySet.forEach(this.onUpdateEntity, this);
		}
	}

	/**
	 * (虚方法)
	 * 场景启动时触发
	 */
	protected onStart?(): void;

	/**
	 * (虚方法)
	 * 匹配的实体添加时
	 */
	protected onEntityAdd?(entity: ECSEntity): void;

	/**
	 * (虚方法)
	 * 匹配的实体移除时
	 */
	protected onEntityRemove?(entity: ECSEntity): void;

	/**
	 * (虚方法)
	 * 当帧循环时触发
	 */
	protected onUpdate() {}

	/**
	 * (虚方法)
	 * 遍历更新过滤器匹配到的实体
	 * 此方法在onUpdate之后触发
	 * @param entity
	 */
	protected onUpdateEntity?(entity: ECSEntity): void;

	/**
	 * (虚方法)
	 * 场景销毁时触发
	 */
	onDestroy?(): void {}

	// ============ DEBUG ============

	/** 记录系统运行花费时间 */
	debugRecord: Map<
		string,
		{
			/** 执行次数 */
			count: number;
			/** 最近一次花费时间 */
			least: number;
			/** 总计花费时间 */
			total: number;
			/** 平均花费时间 */
			avg: number;
		}
	>;

	/**
	 * 开启调试 记录系统运行花费时间
	 */
	enableDebugRecord() {
		if (this.debugRecord) {
			return;
		}

		this.debugRecord = new Map();
		const prefix = TypeUtil.getCls(this).name + "_";

		const record = (fn: string) => {
			const timeKey = prefix + fn;
			const record = { count: 0, least: 0, total: 0, avg: 0 };
			this.debugRecord.set(fn, record);

			const oldFn = (this as any)[fn] as Function;
			(this as any)[fn] = () => {
				TimeUtil.timeStart(timeKey);
				oldFn.call(this);
				++record.count;
				record.least = TimeUtil.timeEnd(timeKey);
				record.total += record.least;
				record.avg = record.total / record.count;
			};
		};

		record("onStart");
		record("onEntityAdd");
		record("onEntityRemove");
		record("onUpdate");
		record("onDestroy");
	}
}
